package consys.social.core.service;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.hbase.util.Bytes;

/**
 * Class SocialUtils contains basic data operations and helpers which we need to store
 * data properly in our nosql database and memcached.
 *
 * @author Petr Kunc
 */
public class SocialUtils {

    /**
     * Converts date to bytes (see dateToString for more information)
     *
     * @param date to convert
     * @return array of bytes
     */
    public static byte[] dateToBytes(Date date) {
        return dateToString(date).getBytes();
    }

    public static Date bytesToDate(byte[] data) throws ParseException {
        return stringToDate(Bytes.toString(data));

    }

    public static Date stringToDate(String data) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddhhmmssSSS");
        sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
        return sdf.parse(data);
    }

    /**
     * Converts date to bytes and then inverts them.
     * We do it because of storing the keys in hbase from newest to oldest
     *
     * @param date to convert
     * @return array of bytes
     */
    public static byte[] dateToBytesReverse(Date date) {
        return invertBytes(dateToBytes(date));
    }

    /**
     * Converts date to String
     * 17 characters
     * first 4 characters are year then 2 month, 2 day, 2 hour, 2 minutes,
     * 2 seconds and 3 miliseconds
     * For example 4/10/2011 22:24:45.178
     * is returned as
     * 20110410222445178
     *
     * @param date
     * @return String in format yyyyMMddhhmmssSSS
     */
    public static String dateToString(Date date) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddhhmmssSSS");
        sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
        return sdf.format(date);
    }

    /**
     * Inverts bytes
     *
     * @param data bytes to invert
     * @return inverted bytes
     */
    public static byte[] invertBytes(byte[] data) {

        int d = 0;
        for (int i = 0; i < data.length; ++i) {
            d = data[i] & 0xFF;
            d = ~d;
            data[i] = (byte) d;
        }

        return data;
    }

    /**
     * generates key that can be stored in sql
     * key in our system is stored as uid:date
     * date we have to invert to store it as newest
     *
     * @param uid string that is used as first part of key (32 characters always)
     * @param date to be used
     * @return byte-key
     */
    public static byte[] generateReverseKey(String uid, Date date) {
        return (byte[]) ArrayUtils.addAll((uid + ":").getBytes(), dateToBytesReverse(date));
    }

    /**
     * very similar to generateReverseKey
     * but this method is used to create "the biggest key" - we use it as the ending point in scanners
     *
     * @param uid string that is used as first part of key (32 characters always)
     * @return "maximum" byte-key for inserted uid
     */
    public static byte[] generateReverseKeyMax(String uid) {
        return (byte[]) ArrayUtils.addAll((uid + ":").getBytes(), invertBytes("00000000000000000".getBytes()));
    }

    /**
     * very similar to generateReverseKey
     * but it doesn't invert date bytes - it is used as memcached string key
     *
     * @param uid string that is used as first part of key (32 characters always)
     * @param date to be used
     * @return byte-key
     */
    public static String generateKey(String uid, Date date) {
        return uid + ":" + dateToString(date);
    }

    public static Date getKeyDate(byte[] key) throws ParseException {
        String keyString = Bytes.toString(invertBytes(key));
        String date = StringUtils.substring(keyString, -17);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddhhmmssSSS");
        sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
        return sdf.parse(date);
    }
}
